From 9255a124934b759b9cc8f5957f96d6145a5b144d Mon Sep 17 00:00:00 2001 From: Matthias Clasen Date: Fri, 8 Feb 2019 18:27:10 -0500 Subject: [PATCH] stack sidebar: Use the selection model Make GtkStackSidebar and GtkStack communicate via the selection model that GtkStack now exposes. This is parallel to the GtkStackSwitcher changes in the previous commit. --- gtk/gtkstacksidebar.c | 251 +++++++++++++++++------------------------- 1 file changed, 103 insertions(+), 148 deletions(-) diff --git a/gtk/gtkstacksidebar.c b/gtk/gtkstacksidebar.c index bf1b13759d..4ac5c7f713 100644 --- a/gtk/gtkstacksidebar.c +++ b/gtk/gtkstacksidebar.c @@ -28,6 +28,7 @@ #include "gtkscrolledwindow.h" #include "gtkseparator.h" #include "gtkstylecontext.h" +#include "gtkselectionmodel.h" #include "gtkprivate.h" #include "gtkintl.h" @@ -58,8 +59,8 @@ struct _GtkStackSidebarPrivate { GtkListBox *list; GtkStack *stack; + GtkSelectionModel *pages; GHashTable *rows; - gboolean in_child_changed; }; G_DEFINE_TYPE_WITH_PRIVATE (GtkStackSidebar, gtk_stack_sidebar, GTK_TYPE_BIN) @@ -129,30 +130,14 @@ sort_list (GtkListBoxRow *row1, GtkListBoxRow *row2, gpointer userdata) { - GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (userdata); - GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - GtkWidget *item; - GtkWidget *widget; gint left = 0; gint right = 0; if (row1) - { - item = gtk_bin_get_child (GTK_BIN (row1)); - widget = g_object_get_data (G_OBJECT (item), "stack-child"); - g_object_get (gtk_stack_get_page (GTK_STACK (priv->stack), widget), - "position", &left, - NULL); - } + left = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row1), "child-index")); if (row2) - { - item = gtk_bin_get_child (GTK_BIN (row2)); - widget = g_object_get_data (G_OBJECT (item), "stack-child"); - g_object_get (gtk_stack_get_page (GTK_STACK (priv->stack), widget), - "position", &right, - NULL); - } + right = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row2), "child-index")); if (left < right) return -1; @@ -170,18 +155,13 @@ gtk_stack_sidebar_row_selected (GtkListBox *box, { GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (userdata); GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - GtkWidget *item; - GtkWidget *widget; - - if (priv->in_child_changed) - return; + guint index; - if (!row) + if (row == NULL) return; - item = gtk_bin_get_child (GTK_BIN (row)); - widget = g_object_get_data (G_OBJECT (item), "stack-child"); - gtk_stack_set_visible_child (priv->stack, widget); + index = GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (row), "child-index")); + gtk_selection_model_select_item (priv->pages, index, TRUE); } static void @@ -218,24 +198,25 @@ gtk_stack_sidebar_init (GtkStackSidebar *sidebar) static void update_row (GtkStackSidebar *sidebar, - GtkWidget *widget, + GtkStackPage *page, GtkWidget *row) { - GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); GtkWidget *item; gchar *title; gboolean needs_attention; + gboolean visible; GtkStyleContext *context; - g_object_get (gtk_stack_get_page (GTK_STACK (priv->stack), widget), + g_object_get (page, "title", &title, "needs-attention", &needs_attention, + "visible", &visible, NULL); item = gtk_bin_get_child (GTK_BIN (row)); gtk_label_set_text (GTK_LABEL (item), title); - gtk_widget_set_visible (row, gtk_widget_get_visible (widget) && title != NULL); + gtk_widget_set_visible (row, visible && title != NULL); context = gtk_widget_get_style_context (row); if (needs_attention) @@ -247,29 +228,19 @@ update_row (GtkStackSidebar *sidebar, } static void -on_position_updated (GtkWidget *widget, - GParamSpec *pspec, - GtkStackSidebar *sidebar) -{ - GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - - gtk_list_box_invalidate_sort (priv->list); -} - -static void -on_child_updated (GtkWidget *widget, - GParamSpec *pspec, - GtkStackSidebar *sidebar) +on_page_updated (GtkStackPage *page, + GParamSpec *pspec, + GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); GtkWidget *row; - row = g_hash_table_lookup (priv->rows, widget); - update_row (sidebar, widget, row); + row = g_hash_table_lookup (priv->rows, page); + update_row (sidebar, page, row); } static void -add_child (GtkWidget *widget, +add_child (guint position, GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); @@ -277,144 +248,139 @@ add_child (GtkWidget *widget, GtkWidget *row; GtkStackPage *page; - /* Check we don't actually already know about this widget */ - if (g_hash_table_lookup (priv->rows, widget)) - return; - /* Make a pretty item when we add kids */ item = gtk_label_new (""); gtk_widget_set_halign (item, GTK_ALIGN_START); gtk_widget_set_valign (item, GTK_ALIGN_CENTER); row = gtk_list_box_row_new (); gtk_container_add (GTK_CONTAINER (row), item); - gtk_widget_show (item); - - update_row (sidebar, widget, row); - - /* Hook up for events */ - page = gtk_stack_get_page (GTK_STACK (priv->stack), widget); - g_signal_connect (widget, "notify::visible", - G_CALLBACK (on_child_updated), sidebar); - g_signal_connect (page, "notify::title", - G_CALLBACK (on_child_updated), sidebar); - g_signal_connect (page, "notify::needs-attention", - G_CALLBACK (on_child_updated), sidebar); - g_signal_connect (page, "notify::position", - G_CALLBACK (on_position_updated), sidebar); - - g_object_set_data (G_OBJECT (item), I_("stack-child"), widget); - g_hash_table_insert (priv->rows, widget, row); + + page = g_list_model_get_item (G_LIST_MODEL (priv->pages), position); + update_row (sidebar, page, row); + gtk_container_add (GTK_CONTAINER (priv->list), row); -} -static void -remove_child (GtkWidget *widget, - GtkStackSidebar *sidebar) -{ - GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - GtkWidget *row; + g_object_set_data (G_OBJECT (row), "child-index", GUINT_TO_POINTER (position)); + if (gtk_selection_model_is_selected (priv->pages, position)) + gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row)); + else + gtk_list_box_unselect_row (priv->list, GTK_LIST_BOX_ROW (row)); - row = g_hash_table_lookup (priv->rows, widget); - if (!row) - return; + g_signal_connect (page, "notify", G_CALLBACK (on_page_updated), sidebar); - if (priv->stack) - { - GtkStackPage *page = gtk_stack_get_page (GTK_STACK (priv->stack), widget); - if (page) - { - g_signal_handlers_disconnect_by_func (page, on_child_updated, sidebar); - g_signal_handlers_disconnect_by_func (page, on_position_updated, sidebar); - } - } + g_hash_table_insert (priv->rows, page, row); - gtk_container_remove (GTK_CONTAINER (priv->list), row); - g_hash_table_remove (priv->rows, widget); + g_object_unref (page); } static void populate_sidebar (GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - GtkWidget *widget, *row; - - gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback)add_child, sidebar); + guint i; - widget = gtk_stack_get_visible_child (priv->stack); - if (widget) - { - row = g_hash_table_lookup (priv->rows, widget); - gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row)); - } + for (i = 0; i < g_list_model_get_n_items (G_LIST_MODEL (priv->pages)); i++) + add_child (i, sidebar); } static void clear_sidebar (GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); + GHashTableIter iter; + GtkStackPage *page; + GtkWidget *row; - gtk_container_foreach (GTK_CONTAINER (priv->stack), (GtkCallback)remove_child, sidebar); + g_hash_table_iter_init (&iter, priv->rows); + while (g_hash_table_iter_next (&iter, (gpointer *)&page, (gpointer *)&row)) + { + gtk_container_remove (GTK_CONTAINER (priv->list), row); + g_hash_table_iter_remove (&iter); + g_signal_handlers_disconnect_by_func (page, on_page_updated, sidebar); + } } static void -on_child_changed (GtkWidget *widget, - GParamSpec *pspec, - GtkStackSidebar *sidebar) +items_changed_cb (GListModel *model, + guint position, + guint removed, + guint added, + GtkStackSidebar *sidebar) +{ + /* FIXME: we can do better */ + clear_sidebar (sidebar); + populate_sidebar (sidebar); +} + +static void +selection_changed_cb (GtkSelectionModel *model, + guint position, + guint n_items, + GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - GtkWidget *child; - GtkWidget *row; + guint i; - child = gtk_stack_get_visible_child (GTK_STACK (widget)); - row = g_hash_table_lookup (priv->rows, child); - if (row != NULL) + for (i = position; i < position + n_items; i++) { - priv->in_child_changed = TRUE; - gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row)); - priv->in_child_changed = FALSE; + GtkStackPage *page; + GtkWidget *row; + + page = g_list_model_get_item (G_LIST_MODEL (priv->pages), i); + row = g_hash_table_lookup (priv->rows, page); + if (gtk_selection_model_is_selected (priv->pages, i)) + gtk_list_box_select_row (priv->list, GTK_LIST_BOX_ROW (row)); + else + gtk_list_box_unselect_row (priv->list, GTK_LIST_BOX_ROW (row)); + g_object_unref (page); } } static void -on_stack_child_added (GtkContainer *container, - GtkWidget *widget, - GtkStackSidebar *sidebar) +disconnect_stack_signals (GtkStackSidebar *sidebar) { - add_child (widget, sidebar); + GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); + + g_signal_handlers_disconnect_by_func (priv->pages, items_changed_cb, sidebar); + g_signal_handlers_disconnect_by_func (priv->pages, selection_changed_cb, sidebar); } static void -on_stack_child_removed (GtkContainer *container, - GtkWidget *widget, - GtkStackSidebar *sidebar) +connect_stack_signals (GtkStackSidebar *sidebar) { - remove_child (widget, sidebar); + GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); + + g_signal_connect (priv->pages, "items-changed", G_CALLBACK (items_changed_cb), sidebar); + g_signal_connect (priv->pages, "selection-changed", G_CALLBACK (selection_changed_cb), sidebar); } static void -disconnect_stack_signals (GtkStackSidebar *sidebar) +set_stack (GtkStackSidebar *sidebar, + GtkStack *stack) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_added, sidebar); - g_signal_handlers_disconnect_by_func (priv->stack, on_stack_child_removed, sidebar); - g_signal_handlers_disconnect_by_func (priv->stack, on_child_changed, sidebar); - g_signal_handlers_disconnect_by_func (priv->stack, disconnect_stack_signals, sidebar); + if (stack) + { + priv->stack = g_object_ref (stack); + priv->pages = gtk_stack_get_pages (stack); + populate_sidebar (sidebar); + connect_stack_signals (sidebar); + } } static void -connect_stack_signals (GtkStackSidebar *sidebar) +unset_stack (GtkStackSidebar *sidebar) { GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); - g_signal_connect_after (priv->stack, "add", - G_CALLBACK (on_stack_child_added), sidebar); - g_signal_connect_after (priv->stack, "remove", - G_CALLBACK (on_stack_child_removed), sidebar); - g_signal_connect (priv->stack, "notify::visible-child", - G_CALLBACK (on_child_changed), sidebar); - g_signal_connect_swapped (priv->stack, "destroy", - G_CALLBACK (disconnect_stack_signals), sidebar); + if (priv->stack) + { + disconnect_stack_signals (sidebar); + clear_sidebar (sidebar); + g_clear_object (&priv->stack); + g_clear_object (&priv->pages); + } } static void @@ -422,7 +388,7 @@ gtk_stack_sidebar_dispose (GObject *object) { GtkStackSidebar *sidebar = GTK_STACK_SIDEBAR (object); - gtk_stack_sidebar_set_stack (sidebar, NULL); + unset_stack (sidebar); G_OBJECT_CLASS (gtk_stack_sidebar_parent_class)->dispose (object); } @@ -487,28 +453,17 @@ void gtk_stack_sidebar_set_stack (GtkStackSidebar *sidebar, GtkStack *stack) { - GtkStackSidebarPrivate *priv; + GtkStackSidebarPrivate *priv = gtk_stack_sidebar_get_instance_private (sidebar); g_return_if_fail (GTK_IS_STACK_SIDEBAR (sidebar)); g_return_if_fail (GTK_IS_STACK (stack) || stack == NULL); - priv = gtk_stack_sidebar_get_instance_private (sidebar); if (priv->stack == stack) return; - if (priv->stack) - { - disconnect_stack_signals (sidebar); - clear_sidebar (sidebar); - g_clear_object (&priv->stack); - } - if (stack) - { - priv->stack = g_object_ref (stack); - populate_sidebar (sidebar); - connect_stack_signals (sidebar); - } + unset_stack (sidebar); + set_stack (sidebar, stack); gtk_widget_queue_resize (GTK_WIDGET (sidebar)); -- 2.30.2